package br.com.d2g.realestate.controller;

import br.com.d2g.realestate.persistence.User;
import br.com.d2g.realestate.controller.util.JsfUtil;
import br.com.d2g.realestate.controller.util.PaginationHelper;
import br.com.d2g.realestate.facade.AgentFacade;
import br.com.d2g.realestate.facade.ClientFacade;
import br.com.d2g.realestate.facade.OwnerFacade;
import br.com.d2g.realestate.facade.UserFacade;

import br.com.d2g.realestate.persistence.Agent;
import br.com.d2g.realestate.persistence.Client;
import br.com.d2g.realestate.persistence.Owner;
import java.io.Serializable;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.faces.model.SelectItem;
import javax.servlet.http.HttpSession;

@ManagedBean(name = "userController")
@SessionScoped
public class UserController implements Serializable {

    private User current;
    private DataModel items = null;
    @EJB
    private ClientFacade clientFacade;
    @EJB
    private AgentFacade agentFacade;
    @EJB
    private OwnerFacade ownerFacade;
    @EJB
    private br.com.d2g.realestate.facade.UserFacade ejbFacade;
    private PaginationHelper pagination;
    private int selectedItemIndex;
    private String username;
    private String password;
    private String firstName;
    private String lastName;
    private String phone;
    private String agencyName;
    private String address;
    private String address2;
    private String postcode;
    
    public UserController() {
    }

    public User getSelected() {
        if (current == null) {
            current = new User();
            selectedItemIndex = -1;
        }
        return current;
    }

    private UserFacade getFacade() {
        return ejbFacade;
    }

    public PaginationHelper getPagination() {
        if (pagination == null) {
            pagination = new PaginationHelper(10) {

                @Override
                public int getItemsCount() {
                    return getFacade().count();
                }

                @Override
                public DataModel createPageDataModel() {
                    return new ListDataModel(getFacade().findRange(new int[]{getPageFirstItem(), getPageFirstItem() + getPageSize()}));
                }
            };
        }
        return pagination;
    }

    public String prepareList() {
        recreateModel();
        return "List";
    }

    public String prepareView() {
        current = (User) getItems().getRowData();
        selectedItemIndex = pagination.getPageFirstItem() + getItems().getRowIndex();
        return "View";
    }

    public String prepareCreate() {
        current = new User();
        selectedItemIndex = -1;
        return "Create";
    }

    public String prepareCreateClient() {
        current = new User();
        selectedItemIndex = -1;
        return "Create";
    }

    public String prepareCreateAgent() {
        current = new User();
        selectedItemIndex = -1;
        return "Create";
    }

    public String prepareCreateOwner() {
        current = new User();
        selectedItemIndex = -1;
        return "Create";
    }

    public String create() {
        try {
            getFacade().create(current);
            JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/Bundle").getString("UserCreated"));
            return prepareCreate();
        } catch (Exception e) {
            JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("PersistenceErrorOccured"));
            return null;
        }
    }

    public String createClient() {
        try {
            current.setPassword(getFacade().cypher(current.getPassword()));
            getFacade().create(current);
            Client client = new Client();
            client.setFirstName(firstName);
            client.setLastName(this.lastName);
            client.setUser(current);
            client.setUsername(current.getUsername());
            clientFacade.create(client);
            JsfUtil.addSuccessMessage("Client successfully created");
            return prepareCreateClient();
        } catch (Exception e) {
            if (e.getCause().getCause().getLocalizedMessage().contains(new StringBuffer("Duplicate entry"))) {
                JsfUtil.addErrorMessage("username already exist, please choose another!");
                return null;
            }
            JsfUtil.addErrorMessage(e.getMessage());
            return null;
        }
    }

    public String createAgent() {
        try {
            current.setPassword(getFacade().cypher(current.getPassword()));
            getFacade().create(current);
            Agent agent = new Agent();
            agent.setFirstName(firstName);
            agent.setLastName(lastName);
            agent.setPhone(phone);
            agent.setAgencyName(agencyName);
            agent.setUsername(current.getUsername());
            agent.setUser(current);
            agentFacade.create(agent);
            JsfUtil.addSuccessMessage("Agent successfully created");
            return prepareCreateAgent();
        } catch (Exception e) {
            if (e.getCause().getCause().getLocalizedMessage().contains(new StringBuffer("Duplicate entry"))) {
                JsfUtil.addErrorMessage("username already exist, please choose another!");
                return null;
            }
            JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("PersistenceErrorOccured"));
            return null;
        }
    }

    public String createOwner() {
        try {
            current.setPassword(getFacade().cypher(current.getPassword()));
            getFacade().create(current);
            Owner owner = new Owner();
            owner.setAddress(address);
            owner.setAddress2(address2);
            owner.setPostcode(postcode);
            owner.setFirstName(firstName);
            owner.setLastName(lastName);
            owner.setPhone(phone);
            owner.setUsername(current.getUsername());
            owner.setUser(current);
            ownerFacade.create(owner);
            JsfUtil.addSuccessMessage("Owner successfully created");
            return prepareCreateOwner();
        } catch (Exception e) {
            if (e.getCause().getCause().getLocalizedMessage().contains(new StringBuffer("Duplicate entry"))) {
                JsfUtil.addErrorMessage("username already exist, please choose another!");
                return null;
            }
            JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("PersistenceErrorOccured"));
            return null;
        }
    }

    public String prepareEdit() {
        current = (User) getItems().getRowData();
        selectedItemIndex = pagination.getPageFirstItem() + getItems().getRowIndex();
        return "Edit";
    }

    public String prepareEdit(User user) {
        current = user;
//        selectedItemIndex = pagination.getPageFirstItem() + getItems().getRowIndex();
        return "Edit";
    }

    public String update() {
        try {
            try {
                current.setPassword(getFacade().cypher(current.getPassword()));
            } catch (NoSuchAlgorithmException ex) {
                JsfUtil.addErrorMessage(ex.getLocalizedMessage());
                Logger.getLogger(UserController.class.getName()).log(Level.SEVERE, null, ex);
            } catch (Exception ex) {
                JsfUtil.addErrorMessage(ex.getLocalizedMessage());
                Logger.getLogger(UserController.class.getName()).log(Level.SEVERE, null, ex);
            }
            getFacade().edit(current);
            JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/Bundle").getString("UserUpdated"));
            return "View";
        } catch (Exception e) {
            JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("PersistenceErrorOccured"));
            return null;
        }
    }

    public String destroy() {
        current = (User) getItems().getRowData();
        selectedItemIndex = pagination.getPageFirstItem() + getItems().getRowIndex();
        performDestroy();
        recreateModel();
        return "List";
    }

    public String destroyAndView() {
        performDestroy();
        recreateModel();
        updateCurrentItem();
        if (selectedItemIndex >= 0) {
            return "View";
        } else {
            // all items were removed - go back to list
            recreateModel();
            return "List";
        }
    }

    private void performDestroy() {
        try {
            getFacade().remove(current);
            JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/Bundle").getString("UserDeleted"));
        } catch (Exception e) {
            JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("PersistenceErrorOccured"));
        }
    }

    private void updateCurrentItem() {
        int count = getFacade().count();
        if (selectedItemIndex >= count) {
            // selected index cannot be bigger than number of items:
            selectedItemIndex = count - 1;
            // go to previous page if last page disappeared:
            if (pagination.getPageFirstItem() >= count) {
                pagination.previousPage();
            }
        }
        if (selectedItemIndex >= 0) {
            current = getFacade().findRange(new int[]{selectedItemIndex, selectedItemIndex + 1}).get(0);
        }
    }

    public String doLogin() {
        User user = new User(getUsername(), getPassword());
        try {
            if (getFacade().doLogin(user)) {
                HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(true);
                session.setAttribute("logged", true);
                session.setAttribute("user", false);
                session.setAttribute("owner", false);
                session.setAttribute("agent", false);
                session.setAttribute("username", getUsername());
                List<String> roles = getFacade().getRoles(user);
                session.setAttribute("roles", roles);
                if (roles.contains("client")) {
                    session.setAttribute("user", true);
                    session.setAttribute("client", true);
                }
                if (roles.contains("owner")) {
                    session.setAttribute("owner", true);
                }
                if (roles.contains("agent")) {
                    session.setAttribute("agent", true);


                }
            }
        } catch (NoSuchAlgorithmException e) {
            Logger.getLogger(UserFacade.class.getName()).log(Level.SEVERE, null, e);
            JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("PersistenceErrorOccured"));


            return null;
        } catch (Exception e) {
//            systemLog.log(ResourceBundle.getBundle("/Bundle").getString("LoginWrongUserPassword"));
            JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("LoginWrongUserPassword"));
            return null;
        }
        return "announce";
    }

    public static boolean loggedIn() {
        HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(true);
        if (session.getAttribute("logged") != null && ((Boolean) session.getAttribute("logged"))) {
            return true;
        }
        return false;
    }

    public static String getLoggedUserName() throws Exception {
        HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(true);
        if (session.getAttribute("username") != null) {
            return (String) session.getAttribute("username");
        }
        throw new Exception("User not logged in");
    }

    public static List<String> getRoles() throws Exception {
        HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(true);
        if (session.getAttribute("roles") != null) {
            return (List<String>) session.getAttribute("roles");
        }
        return null;
    }

    public static Boolean isClient(){
        HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(true);
        if (session.getAttribute("client") != null && (Boolean) session.getAttribute("client")) {
            return true;
        }
        return false;
    }

    public static Boolean isAgent(){
        HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(true);
        if (session.getAttribute("agent") != null && (Boolean) session.getAttribute("agent")) {
            return true;
        }
        return false;
    }

    public static Boolean isOwner() {
        HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(true);
        if (session.getAttribute("owner") != null && (Boolean) session.getAttribute("owner")) {
            return true;
        }
        return false;
    }

    public String doLogout() {
        HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(true);
        session.invalidate();
        return "logout";
    }

    public DataModel getItems() {
        if (items == null) {
            items = getPagination().createPageDataModel();
        }
        return items;
    }

    private void recreateModel() {
        items = null;
    }

    public String next() {
        getPagination().nextPage();
        recreateModel();
        return "List";
    }

    public String previous() {
        getPagination().previousPage();
        recreateModel();
        return "List";
    }

    public SelectItem[] getItemsAvailableSelectMany() {
        return JsfUtil.getSelectItems(ejbFacade.findAll(), false);
    }

    public SelectItem[] getItemsAvailableSelectOne() {
        return JsfUtil.getSelectItems(ejbFacade.findAll(), true);
    }

    /**
     * @return the username
     */
    public String getUsername() {
        return username;
    }

    /**
     * @param username the username to set
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * @return the password
     */
    public String getPassword() {
        return password;
    }

    /**
     * @param password the password to set
     */
    public void setPassword(String password) {
        this.password = password;
    }

    /**
     * @return the firstName
     */
    public String getFirstName() {
        return firstName;
    }

    /**
     * @param firstName the firstName to set
     */
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    /**
     * @return the lastName
     */
    public String getLastName() {
        return lastName;
    }

    /**
     * @param lastName the lastName to set
     */
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    /**
     * @return the phone
     */
    public String getPhone() {
        return phone;
    }

    /**
     * @param phone the phone to set
     */
    public void setPhone(String phone) {
        this.phone = phone;
    }

    /**
     * @return the agencyName
     */
    public String getAgencyName() {
        return agencyName;
    }

    /**
     * @param agencyName the agencyName to set
     */
    public void setAgencyName(String agencyName) {
        this.agencyName = agencyName;
    }

    /**
     * @return the address
     */
    public String getAddress() {
        return address;
    }

    /**
     * @param address the address to set
     */
    public void setAddress(String address) {
        this.address = address;
    }

    /**
     * @return the address2
     */
    public String getAddress2() {
        return address2;
    }

    /**
     * @param address2 the address2 to set
     */
    public void setAddress2(String address2) {
        this.address2 = address2;
    }

    /**
     * @return the postcode
     */
    public String getPostcode() {
        return postcode;
    }

    /**
     * @param postcode the postcode to set
     */
    public void setPostcode(String postcode) {
        this.postcode = postcode;


    }

    @FacesConverter(forClass = User.class)
    public static class UserControllerConverter implements Converter {

        public Object getAsObject(FacesContext facesContext, UIComponent component, String value) {
            if (value == null || value.length() == 0) {
                return null;
            }
            UserController controller = (UserController) facesContext.getApplication().getELResolver().
                    getValue(facesContext.getELContext(), null, "userController");
            return controller.ejbFacade.find(getKey(value));
        }

        java.lang.String getKey(String value) {
            java.lang.String key;
            key = value;
            return key;
        }

        String getStringKey(java.lang.String value) {
            StringBuffer sb = new StringBuffer();
            sb.append(value);
            return sb.toString();
        }

        public String getAsString(FacesContext facesContext, UIComponent component, Object object) {
            if (object == null) {
                return null;
            }
            if (object instanceof User) {
                User o = (User) object;
                return getStringKey(o.getUsername());
            } else {
                throw new IllegalArgumentException("object " + object + " is of type " + object.getClass().getName() + "; expected type: " + UserController.class.getName());
            }
        }
    }
}
